#error THIS IS HERE AS A DEMO FILE - DO NOT TRY TO COMPILE THIS
/******************************************************************************
*	device/hid/hid.c
*	 by Alex Chadwick
*
*	A light weight implementation of the USB protocol stack fit for a simple
*	driver.
*
*	device/hid/hid.c contains code relating to the generic USB human interface
*	device driver. Human interface devices have another standard on top of USB
*	(oh boy!) which is actually very neat. It allows human interface devices to
*	describe their buttons, sliders and dials in great detail, and allows a
*	flexible driver to handle them all. This driver merely provides methods to
*	deal with these reports. More abstracted drivers for keyboards and mice and
*	whatnot would no doubt be very useful.
******************************************************************************/
#include <device/hid/hid.h>
#include <device/hid/report.h>
#include <platform/platform.h>
#include <usbd/descriptors.h>
#include <usbd/device.h>
#include <types.h>
#include <usbd/usbd.h>

#define  HidMessageTimeout  (10)

Result (*HidUsageAttach[HidUsageAttachCount])(UsbDevice *device, u32 interfaceNumber);

//------------------------------------------------------------------------------
typedef
	struct Fields {
		HidParserResult  *result;
		u32              count;
		u32              size;
		HidFullUsage     *usage;
		HidFullUsage     physical;
		s32              logicalMinimum;
		s32              logicalMaximum;
		s32              physicalMinimum;
		s32              physicalMaximum;
		HidUnit          unit;
		s32              unitExponent;
		HidUsagePage     page;
		u8               report;
	}
Fields;

//------------------------------------------------------------------------------
typedef
	struct ReportFieldsInner {
		u8             reportId;
		u8             fieldCount;
		HidReportType  type;
	}
ReportFieldsInner;

typedef
	struct ReportFields {
		u32                count;
		u8                 current;
		u8                 report;
		ReportFieldsInner  reports[];
	}
ReportFields;

//------------------------------------------------------------------------------
typedef
	struct Reports {
		u8    reportCount;
		u8    indent;
		bool  input;
		bool  output;
		bool  feature;
	}
Reports;

//+=============================================================================
// Func: HidLoad
// Desc:
// Args: -none-
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidLoad (void)
{
	LOG_DEBUG("CSUD: HID driver version 0.1\n");
	InterfaceClassAttach[InterfaceClassHid] = HidAttach;
}

//+=============================================================================
// Func: HidGetReport
// Desc: Perform a hid get report request and retrieve a hid report.
// Args: UsbDevice*    device       ..
//     : HidReportType reportType   ..
//     : u8            reportId     ..
//     : u8            Interface    ..
//     : u32           bufferLength ..
//     : void*         buffer       ..
// Retn: Result ..
// Note: Defined in "HID Specification, Ver 1,11 27/Jun/2001" manual in 7.2.1
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidGetReport (UsbDevice* device, HidReportType reportType,
                      u8 reportId, u8 interface, u32 bufferLength, void* buffer)
{
	return UsbControlMessage(
		device,
		(UsbPipeAddress) {
			.Type      = Control,
			.Speed     = device->Speed,
			.EndPoint  = 0,
			.Device    = device->Number,
			.Direction = In,
			.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0),
		},
		buffer, bufferLength,
		&(UsbDeviceRequest) {
			.Request = GetReport,
			.Type    = 0xa1,
			.Index   = interface,
			.Value   = (u16)reportType << 8 | reportId,
			.Length  = bufferLength,
		},
		HidMessageTimeout );
}

//+=============================================================================
// Func: HidSetReport
// Desc: Send a hid report.
// Args: UsbDevice*    device       ..
//     : HidReportType reportType   ..
//     : u8            reportId     ..
//     : u8            Interface    ..
//     : u32           bufferLength ..
//     : void*         buffer       ..
// Retn: Result ..
// Note: Defined in "HID Specification, Ver 1,11 27/Jun/2001" manual in 7.2.2
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidSetReport (UsbDevice* device, HidReportType reportType,
                      u8 reportId, u8 interface, u32 bufferLength, void* buffer)
{
	return UsbControlMessage(
		device,
		(UsbPipeAddress) {
			.Type      = Control,
			.Speed     = device->Speed,
			.EndPoint  = 0,
			.Device    = device->Number,
			.Direction = Out,
			.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0),
		},
		buffer, bufferLength,
		&(UsbDeviceRequest) {
			.Request = SetReport,
			.Type    = 0x21,
			.Index   = interface,
			.Value   = (u16)reportType << 8 | reportId,
			.Length  = bufferLength,
		},
		HidMessageTimeout );
}

//+=============================================================================
// Func: BitSet
// Desc: Write a value to a bit-offset within a buffer
// Args: void* buffer .. Start of write buffer
//     : u32   offset .. First bit to be altered [1]
//     : u32   length .. Number of bits to be altered
//     : u32   value  .. Value (bit-pattern) to be written [2]
// Retn: -none-
// Note: [1] Bits are numbered left-to-right from 1. EG:
//           The left-most  bit of the first  byte is bit-0
//           The right-most bit of the second byte is bit-15
//     : [2] If you pass a bit-pattern instead of a value, note that it must
//           be held in the least-significant bits of the u32 value field
// Auth: Chadderz; BlueChip
//==============================================================================
void  BitSet (void* buffer, u32 offset, u32 length, u32 value)
{
	u8* bitBuffer;
	u8 mask;

	bitBuffer = buffer;
	for (u32 i = offset / 8, j = 0; i < (offset + length + 7) / 8; i++) {
		if (offset / 8 == (offset + length - 1) / 8) {
			mask = (1 << ((offset % 8) + length)) - (1 << (offset % 8));
			bitBuffer[i] = (bitBuffer[i] & ~mask) |
				((value << (offset % 8)) & mask);
		} else if (i == offset / 8) {
			mask = 0x100 - (1 << (offset % 8));
			bitBuffer[i] = (bitBuffer[i] & ~mask) |
				((value << (offset % 8)) & mask);
			j += 8 - (offset % 8);
		} else if (i == (offset + length - 1) / 8) {
			mask = (1 << ((offset % 8) + length)) - 1;
			bitBuffer[i] = (bitBuffer[i] & ~mask) |
				((value >> j) & mask);
		} else {
			bitBuffer[i] = (value >> j) & 0xff;
			j += 8;
		}
	}
}

//+=============================================================================
// Func: BitGetUnsigned
// Desc: Read a value/bit-pattern of length-bits from bit-offset within a buffer
// Args: void* buffer .. Start of memory buffer
//     : u32   offset .. First bit to be read [1]
//     : u32   length .. Number of bits to be read
// Retn: u32 .. Value/bit-pattern read from buffer
// Note: [1] Bits are numbered left-to-right from 1. EG:
//           The left-most  bit of the first  byte is bit-0
//           The right-most bit of the second byte is bit-15
// Auth: Chadderz; BlueChip
//==============================================================================
u32  BitGetUnsigned (void* buffer, u32 offset, u32 length)
{
	u8* bitBuffer;
	u8 mask;
	u32 result;

	bitBuffer = buffer;
	result = 0;
	for (u32 i = offset / 8, j = 0; i < (offset + length + 7) / 8; i++) {
		if (offset / 8 == (offset + length - 1) / 8) {
			mask = (1 << ((offset % 8) + length)) - (1 << (offset % 8));
			result = (bitBuffer[i] & mask) >> (offset % 8);
		} else if (i == offset / 8) {
			mask = 0x100 - (1 << (offset % 8));
			j += 8 - (offset % 8);
			result = ((bitBuffer[i] & mask) >> (offset % 8)) << (length - j);
		} else if (i == (offset + length - 1) / 8) {
			mask = (1 << ((offset % 8) + length)) - 1;
			result |= bitBuffer[i] & mask;
		} else {
			j += 8;
			result |= bitBuffer[i] << (length - j);
		}
	}

	return result;
}

//+=============================================================================
// Func: BitGetSigned
// Desc: Read a signed-value of length-bits from a bit-offset within a buffer
// Args: void* buffer .. Start of memory buffer
//     : u32   start  .. First bit to be read [1]
//     : u32   length .. Number of bits to be read
// Retn: s32 .. Sign-extended value/bit-pattern read from buffer
// Note: [1] Bits are numbered left-to-right from 1. EG:
//           The left-most  bit of the first  byte is bit-0
//           The right-most bit of the second byte is bit-15
//     : Sign-extending: All bits in the variable to the left of 'the leftmost
//         bit of the result' are copies of 'the leftmost bit of the result'.
// Auth: Chadderz; BlueChip
//==============================================================================
s32  BitGetSigned (void* buffer, u32 offset, u32 length)
{
	u32 result = BitGetUnsigned(buffer, offset, length);

	if (result & (1 << (length - 1)))
		result |= 0xffffffff - ((1 << length) - 1);

	return result;
}

//+=============================================================================
// Func: HidReadDevice
// Desc: Updates a report with the values from the device.
// Args: UsbDevice* device       ..
//     : u8         reportNumber ..
// Retn: Result ..
// Note: Reads the current values of a report from the device into memory.
//     : Implemented using HidGetReport not interrupts.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidReadDevice (UsbDevice* device, u8 reportNumber)
{
	HidDevice        *data;
	HidParserResult  *parse;
	HidParserReport  *report;
	HidParserField   *field;
	Result           result;
	u32              i,  j,  size;

	data   = (HidDevice*)device->DriverData;
	parse  = data->ParserResult;
	report = parse->Report[reportNumber];
	size   = (report->ReportLength + 7) / 8;

	if (report->ReportBuffer == NULL) {
		report->ReportBuffer = (u8*)MemoryAllocate(size);
		if (report->ReportBuffer == NULL)  return ErrorMemory ;
	}

	result = HidGetReport(device, report->Type, report->Id,
	                      data->ParserResult->Interface, size, report->ReportBuffer);
	if (result != OK) {
		if (result != ErrorDisconnected)
			LOGF("HID: Could not read %s report %d.\n", UsbGetDescription(device), report);
		return result;
	}

#if 0  // Change to '1' for a quick hack to view 8 bytes worth of report.
	LOGF("HID: %s.Report%d: %02x%02x%02x%02x %02x%02x%02x%02x.\n",
	     UsbGetDescription(device), reportNumber + 1,
		 *(report->ReportBuffer + 0), *(report->ReportBuffer + 1),
	     *(report->ReportBuffer + 2), *(report->ReportBuffer + 3),
		 *(report->ReportBuffer + 4), *(report->ReportBuffer + 5),
	     *(report->ReportBuffer + 6), *(report->ReportBuffer + 7));
#endif

	for (i = 0; i < report->FieldCount; i++) {
		field = &report->Fields[i];
		if (field->Attributes.Variable) {
			if (field->LogicalMinimum < 0)
				field->Value.S32 = BitGetSigned(report->ReportBuffer, field->Offset, field->Size);
			else
				field->Value.U32 = BitGetUnsigned(report->ReportBuffer, field->Offset, field->Size);

		} else {
			for (j = 0; j < field->Count; j++)
//!yuk
				BitSet(
						field->Value.Pointer,
						j * field->Size,
						field->Size,
						field->LogicalMinimum < 0 ? BitGetSigned(
							report->ReportBuffer,
							field->Offset + j * field->Size,
							field->Size
						) : BitGetUnsigned(
							report->ReportBuffer,
							field->Offset + j * field->Size,
							field->Size
						)
					);
		}
	}

	return OK;
}
//+=============================================================================
// Func: HidWriteDevice
// Desc: Updates the device with the values of a report.
// Args: UsbDevice* device       ..
//     : u8         reportNumber ..
// Retn: Result ..
// Note: Writes back the current values of a report in memory to the device.
//     : Implemented using HidSetReport, not interrupts.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidWriteDevice (UsbDevice* device, u8 reportNumber)
{
	HidDevice        *data;
	HidParserResult  *parse;
	HidParserReport  *report;
	HidParserField   *field;
	Result           result;
	u32              i,  size;

	data   = (HidDevice*)device->DriverData;
	parse  = data->ParserResult;
	report = parse->Report[reportNumber];
	size   = (report->ReportLength + 7) / 8;

	if (report->ReportBuffer == NULL) {
		report->ReportBuffer = (u8*)MemoryAllocate(size);
		if (report->ReportBuffer == NULL)  return ErrorMemory ;
	}

	for (i = 0; i < report->FieldCount; i++) {
		field = &report->Fields[i];
		if (field->Attributes.Variable) {
			BitSet(report->ReportBuffer, field->Offset, field->Size, field->Value.S32);
		} else {
			for (u32 j = 0; j < field->Count; j++)
//!yuk
				BitSet(
						report->ReportBuffer,
						field->Offset + j * field->Size,
						field->Size,
						BitGetSigned(
							field->Value.Pointer,
							j * field->Size,
							field->Size
						)
					);
		}
	}

	result = HidSetReport(device, report->Type, report->Id,
	                      data->ParserResult->Interface, size, report->ReportBuffer);
	if (result != OK) {
		if (result != ErrorDisconnected)
			LOGF("HID: Coult not read %s report %d.\n", UsbGetDescription(device), report);
		return result;
	}

	return OK;
}

//+=============================================================================
// Func: HidSetProtocol
// Desc:
// Args: UsbDevice* device    ..
//     : u8         interface ..
//     : u16        protocol  ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidSetProtocol (UsbDevice* device, u8 interface, u16 protocol)
{
	return UsbControlMessage(
		device,
		(UsbPipeAddress) {
			.Type      = Control,
			.Speed     = device->Speed,
			.EndPoint  = 0,
			.Device    = device->Number,
			.Direction = Out,
			.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0),
		},
		NULL, 0, // No buffer (of length 0)
		&(UsbDeviceRequest) {
			.Request = SetProtocol,
			.Type    = 0x21,
			.Index   = interface,
			.Value   = protocol,
			.Length  = 0,
		},
		HidMessageTimeout );
}

//+=============================================================================
// Func: HidEnumerateReport
// Desc:
// Args: void*                    descriptor ..
//     : u16                      length     ..
//     : void(*)(void*, u16, u32) action     ..
//     : void*                    data       ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidEnumerateReport (void* descriptor, u16 length,
                          void(*action)(void* data, u16 tag, u32 value),
                          void* data)
{
	HidReportItem  *item,  *current;
	u16            parsedLength,  currentIndex,  currentLength;
	s32            value;

	// tags for short items will be stored in the low  6  bits
	// tags for long  items will be stored in the top  8  bits
	u16            tag;

	item         = descriptor;
	current      = NULL;

	for (parsedLength = 0;  parsedLength < length;  parsedLength++) {
		if (current == NULL) {
			current       = item;
			currentIndex  = 0;
			currentLength = 1 << (current->Size - 1);
			value         = 0;
			tag           = current->Tag;
			if (currentLength == 0)  current = NULL ;

		} else {
			if ((current->Tag == TagLong) && (currentIndex < 2)) {
				if (currentIndex == 0)  currentLength += *(u8*)item ;
				else                    tag           |= (u16)*(u8*)item << 8;
			} else {
				value |= (u32)*(u8*)item << (8 * currentIndex);
			}
			if (++currentIndex == currentLength)  current = NULL ;
		}

		if (current == NULL) {
			if ((tag & 0x3) == 0x1) {
				if      ((currentLength == 1) && (value & 0x80))    value |= 0xFFFFFF00 ;
				else if ((currentLength == 2) && (value & 0x8000))  value |= 0xFFFF0000 ;
			}
			action(data, tag, value);
		}

		item++;
	}
}

//+=============================================================================
// Func: HidEnumerateActionCountReport
// Desc:
// Args: void* data  ..
//     : u16   tag   ..
//     : u32   value ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidEnumerateActionCountReport (void* data, u16 tag, u32 value)
{
	Reports  *reports = data;

	switch (tag) {
		case TagMainInput:
			if (!reports->input)  {
				reports->reportCount++;
				reports->input = true;
			}
			LOG_DEBUGF("HID: %.*sInput(%03o)\n",
			           reports->indent, "           ", value);
			break;

		case TagMainOutput:
			if (!reports->output)  {
				reports->reportCount++;
				reports->output = true;
			}
			LOG_DEBUGF("HID: %.*sOutput(%03o)\n",
			           reports->indent, "           ", value);
			break;

		case TagMainFeature:
			if (!reports->feature)  {
				reports->reportCount++;
				reports->feature = true;
			}
			LOG_DEBUGF("HID: %.*sFeature(%03o)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalReportId:
			reports->input   =  false;
			reports->output  =  false;
			reports->feature =  false;
			LOG_DEBUGF("HID: %.*sReport ID(%d)\n",
			           reports->indent, "           ", value);
			break;

		default:
			LOG_DEBUGF("HID: Unexpected tag in report %d = %x.\n", tag, value);
			break;

#if CONFIG_DEBUG
		case TagMainCollection:
			LOG_DEBUGF("HID: %.*sCollection(%d)\n",
			           reports->indent, "           ", value);
			reports->indent++;
			break;

		case TagMainEndCollection:
			reports->indent--;
			LOG_DEBUGF("HID: %.*sEnd Collection\n",
			           reports->indent, "           ");
			break;

		case TagGlobalUsagePage:
			LOG_DEBUGF("HID: %.*sUsage Page(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalLogicalMinimum:
			LOG_DEBUGF("HID: %.*sLogical Minimum(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalLogicalMaximum:
			LOG_DEBUGF("HID: %.*sLogical Maximum(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalPhysicalMinimum:
			LOG_DEBUGF("HID: %.*sPhysical Minimum(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalPhysicalMaximum:
			LOG_DEBUGF("HID: %.*sPhysical Maximum(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalUnitExponent:
			LOG_DEBUGF("HID: %.*sUnit Exponent(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalUnit:
			LOG_DEBUGF("HID: %.*sUnit(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalReportSize:
			LOG_DEBUGF("HID: %.*sReport Size(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalReportCount:
			LOG_DEBUGF("HID: %.*sReport Count(%d)\n",
			           reports->indent, "           ", value);
			break;

		case TagGlobalPush:
			LOG_DEBUGF("HID: %.*sPush\n",
			           reports->indent, "           ");
			break;

		case TagGlobalPop:
			LOG_DEBUGF("HID: %.*sPop\n",
			           reports->indent, "           ");
			break;

		case TagLocalUsage:
			LOG_DEBUGF("HID: %.*sUsage(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalUsageMinimum:
			LOG_DEBUGF("HID: %.*sUsage Minimum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalUsageMaximum:
			LOG_DEBUGF("HID: %.*sUsage Maximum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalDesignatorIndex:
			LOG_DEBUGF("HID: %.*sDesignator Index(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalDesignatorMinimum:
			LOG_DEBUGF("HID: %.*sDesignator Minimum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalDesignatorMaximum:
			LOG_DEBUGF("HID: %.*sDesignator Maximum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalStringIndex:
			LOG_DEBUGF("HID: %.*sString Index(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalStringMinimum:
			LOG_DEBUGF("HID: %.*sString Minimum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalStringMaximum:
			LOG_DEBUGF("HID: %.*sString Maximum(%u)\n",
			           reports->indent, "           ", value);
			break;

		case TagLocalDelimiter:
			LOG_DEBUGF("HID: %.*sDelimiter\n",
			           reports->indent, "           ");
			break;
#endif // CONFIG_DEBUG
	}
}

//+=============================================================================
// Func: HidEnumerateActionCountField
// Desc:
// Args: void* data  ..
//     : u16   tag   ..
//     : u32   value ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidEnumerateActionCountField (void* data, u16 tag, u32 value)
{
	u32                i;
	ReportFieldsInner  *inner;
	HidReportType      type;
	ReportFields       *reportFields = data;

	type = 0;
	switch (tag) {
		case TagMainFeature:
			type++;
			// Fallthrough to TagMainOutput
		case TagMainOutput:
			type++;
			// Fallthrough to TagMainInput
		case TagMainInput:
			type++;
			inner = NULL;
			for (i = 0; i < reportFields->current; i++)
				if ((reportFields->reports[i].reportId == reportFields->report) &&
				    (reportFields->reports[i].type == type)) {
					inner = &reportFields->reports[i];
					break;
				}

			if (inner == NULL) {
				inner             = &reportFields->reports[reportFields->current++];
				inner->reportId   = reportFields->report;
				inner->fieldCount = 0;
				inner->type       = type;
			}
			if (((HidMainItem*)&value)->Variable)  inner->fieldCount += reportFields->count ;
			else                                   inner->fieldCount++ ;
			break;

		case TagGlobalReportCount:
			reportFields->count = value;
			break;

		case TagGlobalReportId:
			reportFields->report = value;
			break;

		default:
			break;
	}
}

//+=============================================================================
// Func: HidEnumerateActionAddField
// Desc:
// Args: void* data  ..
//     : u16   tag   ..
//     : u32   value ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidEnumerateActionAddField (void* data, u16 tag, u32 value)
{
	u32              i;
	Fields           *fields = data;
	HidReportType    type;
	HidParserReport  *report;

	type = 0;
	switch (tag) {
		case TagMainFeature:
			type++;
			// Fallthrough to TagMainOutput
		case TagMainOutput:
			type++;
			// Fallthrough to TagMainInput
		case TagMainInput:
			type++;
			report = NULL;
			for (i = 0; i < fields->result->ReportCount; i++)
				if ((fields->result->Report[i]->Id == fields->report) &&
				    (fields->result->Report[i]->Type == type)) {
					report = fields->result->Report[i];
					break;
				}
			while (fields->count > 0) {
				HidParserField  *pField = &report->Fields[report->FieldCount];

				if (*(u32*)fields->usage == 0xFFFFFFFF) fields->usage++;
				*(u32*)&(pField->Attributes) = value;
				pField->Count                 = pField->Attributes.Variable ? 1 : fields->count;
				pField->LogicalMaximum        = fields->logicalMaximum;
				pField->LogicalMinimum        = fields->logicalMinimum;
				pField->Offset                = report->ReportLength;
				pField->PhysicalMaximum       = fields->physicalMaximum;
				pField->PhysicalMinimum       = fields->physicalMinimum;
				*(u32*)&pField->PhysicalUsage = *(u32*)&fields->physical;
				pField->Size                  = fields->size;
				*(u32*)&pField->Unit          = *(u32*)&fields->unit;
				pField->UnitExponent          = fields->unitExponent;

				if ((u16)fields->usage->Page != 0xFFFF) {
					*(u32*)&pField->Usage = *(u32*)(fields->usage--);

				} else {
					*(u32*)&pField->Usage = *(u32*)&fields->usage[-1];

					if ((fields->usage->Desktop == fields->usage[-1].Desktop) ||
					    !pField->Attributes.Variable)
						fields->usage -= 2;
					else
						fields->usage[-1].Desktop++;
				}

				if (pField->Attributes.Variable) {
					fields->count--;
					report->ReportLength += pField->Size;
					pField->Value.U32 = 0;
				} else {
					fields->count = 0;
					report->ReportLength += pField->Size * pField->Count;
					pField->Value.Pointer = MemoryAllocate(pField->Size * pField->Count / 8);
				}
				report->FieldCount++;
			}
			*(u32*)&fields->usage[1] = 0;
			break;

		case TagMainCollection:
			if (*(u32*)fields->usage == 0xFFFFFFFF) fields->usage++;
			switch ((HidMainCollection)value) {
				case Application:
					*(u32*)&fields->result->Application = *(u32*)fields->usage;
					break;
				case Physical:
					*(u32*)&fields->physical = *(u32*)fields->usage;
					break;
				default:
					break;
			}
			fields->usage--;
			break;

		case TagMainEndCollection:
			// I assume this is a switch() to allow for future expansion !?
			switch ((HidMainCollection)value) {
				case Physical:
					// Write 0 to the first 32-bits of fields->physical
					*(u32*)&fields->physical = 0;
					break;
				default:
					break;
			}
			break;

		case TagGlobalUsagePage:
			fields->page = (HidUsagePage)value;
			break;

		case TagGlobalLogicalMinimum:
			fields->logicalMinimum = value;
			break;

		case TagGlobalLogicalMaximum:
			fields->logicalMaximum = value;
			break;

		case TagGlobalPhysicalMinimum:
			fields->physicalMinimum = value;
			break;

		case TagGlobalPhysicalMaximum:
			fields->physicalMaximum = value;
			break;

		case TagGlobalUnitExponent:
			fields->unitExponent = value;
			break;

		case TagGlobalUnit:
			*(u32*)&fields->unit = value;
			break;

		case TagGlobalReportSize:
			fields->size = value;
			break;

		case TagGlobalReportId:
			fields->report = (u8)value;
			break;

		case TagGlobalReportCount:
			fields->count = value;
			break;

		case TagLocalUsage:
			fields->usage++;
			if (value & 0xFFFF0000) {  // Top 16bits of 'value' are non-zero
				*(u32*)&fields->usage = value;

			} else {                   // Top 16bits of 'value' are zero
				fields->usage->Desktop = (HidUsagePageDesktop)value;
				fields->usage->Page    = fields->page;
			}
			break;

		case TagLocalUsageMinimum:
			fields->usage++;
			if (value & 0xFFFF0000) {  // Top 16bits of 'value' are non-zero
				*(u32*)&fields->usage = value;

			} else {                   // Top 16bits of 'value' are zero
				fields->usage->Desktop = (HidUsagePageDesktop)value;
				fields->usage->Page = fields->page;
			}
			break;

		case TagLocalUsageMaximum:
			fields->usage++;
			fields->usage->Desktop = (HidUsagePageDesktop)value;
			fields->usage->Page    = (HidUsagePage)0xFFFF;
			break;

		default:
			break;
	}
}

//+=============================================================================
// Func: HidParseReportDescriptor
// Desc:
// Args: UsbDevice* device     ..
//     : void*      descriptor ..
//     : u16        length     ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidParseReportDescriptor (UsbDevice* device,
                                  void* descriptor, u16 length)
{
	u32              i;
	Result           result;
	HidDevice        *data;

	HidParserResult  *parse        = NULL;
	ReportFields     *reportFields = NULL;
	Fields           *fields       = NULL;
	void*            usageStack    = NULL;

	Reports          reports = { .reportCount = 0,
	                             .indent      = 0,
	                             .input       = false,
	                             .output      = false,
	                             .feature     = false
	                           };

	data = (HidDevice*)device->DriverData;

	HidEnumerateReport(descriptor, length, HidEnumerateActionCountReport, &reports);
	LOG_DEBUGF("HID: Found %d reports.", reports.reportCount);

	parse = MemoryAllocate(sizeof(HidParserResult) + 4 * reports.reportCount);
	if (parse == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}

	for (i = 0; i < reports.reportCount; i++)  parse->Report[i] = NULL ;

	reportFields = MemoryAllocate(sizeof(ReportFields) + (sizeof(ReportFieldsInner) * reports.reportCount));
	if (reportFields == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}

	parse->ReportCount    = reports.reportCount;
	reportFields->count   = 0;
	reportFields->current = 0;
	reportFields->report  = 0;

	HidEnumerateReport(descriptor, length, HidEnumerateActionCountField, reportFields);
	for (i = 0; i < reports.reportCount; i++) {
		parse->Report[i] = MemoryAllocate(sizeof(HidParserReport) + sizeof(HidParserField) * reportFields->reports[i].fieldCount);
		if (parse->Report[i] == NULL) {
			result = ErrorMemory;
			goto deallocate;
		}
		parse->Report[i]->Index       = i;
		parse->Report[i]->FieldCount   = 0;
		parse->Report[i]->Id           = reportFields->reports[i].reportId;
		parse->Report[i]->Type         = reportFields->reports[i].type;
		parse->Report[i]->ReportLength = 0;
		parse->Report[i]->ReportBuffer = NULL;
	}
	MemoryDeallocate(reportFields);
	reportFields = NULL;

	fields = MemoryAllocate(sizeof(Fields));
	if (fields == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}

	fields->usage = usageStack = MemoryAllocate(16 * sizeof(HidFullUsage*));
	if (fields->usage == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}
	fields->count           = 0;
	fields->logicalMaximum  = 0;
	fields->logicalMinimum  = 0;
	fields->physicalMaximum = 0;
	fields->physicalMinimum = 0;
	fields->report          = 0;
	fields->size            = 0;
	*(u32*)fields->usage    = 0xFFFFFFFF;
	fields->result          = parse;
	HidEnumerateReport(descriptor, length, HidEnumerateActionAddField, fields);

	data->ParserResult = parse;
	parse              = NULL;
	result             = OK;

deallocate:
	if (usageStack   != NULL) MemoryDeallocate(usageStack);
	if (fields       != NULL) MemoryDeallocate(fields);
	if (reportFields != NULL) MemoryDeallocate(reportFields);
	if (parse        != NULL) {
		for (i = 0; i < reports.reportCount; i++)
			if (parse->Report[i] != NULL)  MemoryDeallocate(parse->Report[i]) ;
		MemoryDeallocate(parse);
	}

	return result;
}

//+=============================================================================
// Func: HidDetached
// Desc:
// Args: UsbDevice* device ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidDetached (UsbDevice* device)
{
	HidDevice  *data;

	if (device->DriverData != NULL) {
		data = (HidDevice*)device->DriverData;
		if (data->HidDetached != NULL)  data->HidDetached(device) ;
	}
}

//+=============================================================================
// Func: HidDeallocate
// Desc:
// Args: UsbDevice* device ..
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  HidDeallocate (UsbDevice* device)
{
	u32              i,  j;
	HidDevice        *data;
	HidParserReport  *report;

	if (device->DriverData != NULL) {
		data = (HidDevice*)device->DriverData;

		if (data->HidDeallocate != NULL)  data->HidDeallocate(device) ;

		if (data->ParserResult != NULL) {
			for (i = 0; i < data->ParserResult->ReportCount; i++) {
				if (data->ParserResult->Report[i] != NULL) {
					report = data->ParserResult->Report[i];
					if (report->ReportBuffer != NULL)
						MemoryDeallocate(report->ReportBuffer);
					for (j = 0; j < report->FieldCount; j++)
						if (!report->Fields[j].Attributes.Variable)
							MemoryDeallocate(report->Fields[j].Value.Pointer);
					MemoryDeallocate(data->ParserResult->Report[i]);
				}
			}
			MemoryDeallocate(data->ParserResult);
		}
		MemoryDeallocate(data);
	}
	device->DeviceDeallocate = NULL;
	device->DeviceDetached   = NULL;

	return;
}

//+=============================================================================
// Func: HidAttach
// Desc: Enumerates a device as a HID device.
// Args: UsbDevice* device          ..
//     : u32        interfaceNumber ..
// Retn: Result ..
// Note: Checks a device to see if it appears to be a HID device, and,
//         if so, loads its hid and report descriptors to see what it can do.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  HidAttach (UsbDevice* device, u32 interfaceNumber)
{
	HidDevice            *data;
	HidDescriptor        *descriptor;
	UsbDescriptorHeader  *header;
	void                 *reportDescriptor = NULL;
	Result               result;
	u32                  currentInterface;

	if (device->Interfaces[interfaceNumber].Class != InterfaceClassHid) {
		return ErrorArgument;
	}

	if (device->Interfaces[interfaceNumber].EndpointCount < 1) {
		LOGF("HID: Invalid HID device with fewer than one endpoints (%d).\n",
		     device->Interfaces[interfaceNumber].EndpointCount);
		return ErrorIncompatible;
	}

	if ((device->Endpoints[interfaceNumber][0].EndpointAddress.Direction != In) ||
	    (device->Endpoints[interfaceNumber][0].Attributes.Type != Interrupt)) {
		LOG("HID: Invalid HID device with unusual endpoints (0).\n");
		return ErrorIncompatible;
	}

	if (device->Interfaces[interfaceNumber].EndpointCount >= 2) {
		if ((device->Endpoints[interfaceNumber][1].EndpointAddress.Direction != Out) ||
		    (device->Endpoints[interfaceNumber][1].Attributes.Type != Interrupt)) {
			LOG("HID: Invalid HID device with unusual endpoints (1).\n");
			return ErrorIncompatible;
		}
	}

	if (device->Status != Configured) {
		LOG("HID: Cannot start driver on unconfigured device!\n");
		return ErrorDevice;
	}

	if (device->Interfaces[interfaceNumber].SubClass == 1) {
		if (device->Interfaces[interfaceNumber].Protocol == 1)
			LOG_DEBUG("HID: Boot keyboard detected.\n");
		else if (device->Interfaces[interfaceNumber].Protocol == 2)
			LOG_DEBUG("HID: Boot mouse detected.\n");
		else
			LOG_DEBUG("HID: Unknown boot device detected.\n");

		LOG_DEBUG("HID: Reverting from boot to normal HID mode.\n");

		result = HidSetProtocol(device, interfaceNumber, 1);
		if (result != OK) {
			LOG("HID: Could not revert to report mode from HID mode.\n");
			return result;
		}
	}

	header           = (UsbDescriptorHeader*)device->FullConfiguration;
	descriptor       = NULL;
	currentInterface = interfaceNumber + 1; // Certainly different!
	do {
		if (header->DescriptorLength == 0)  break ;  // List end
		switch (header->DescriptorType) {
			case Interface:
				currentInterface = ((UsbInterfaceDescriptor*)header)->Number;
				break;
			case Hid:
				if (currentInterface == interfaceNumber)  descriptor = (void*)header ;
				break;
			default:
				break;
		}

		LOG_DEBUGF("HID: Descriptor %d length %d, interface %d.\n",
		           header->DescriptorType, header->DescriptorLength, currentInterface);

		if (descriptor != NULL)  break ;
		header = (void*)((u8*)header + header->DescriptorLength);
	} while (true);

	if (descriptor == NULL) {
		LOGF("HID: No HID descriptor in %s.Interface%d. Cannot be a HID device.\n",
		     UsbGetDescription(device), interfaceNumber + 1);
		return ErrorIncompatible;
	}

	if (descriptor->HidVersion > 0x111) {
		LOGF("HID: Device uses unsupported HID version %x.%x.\n",
		     descriptor->HidVersion >> 8, descriptor->HidVersion & 0xFF);
		return ErrorIncompatible;
	}
	LOG_DEBUGF("HID: Device version HID %x.%x.\n",
	           descriptor->HidVersion >> 8, descriptor->HidVersion & 0xFF);

	device->DeviceDeallocate = HidDeallocate;
	device->DeviceDetached   = HidDetached;
	device->DriverData       = MemoryAllocate(sizeof(HidDevice));
	if (device->DriverData == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}
	device->DriverData->DataSize     = sizeof(HidDevice);
	device->DriverData->DeviceDriver = DeviceDriverHid;
	data                             = (HidDevice*)device->DriverData;
	data->Descriptor                 = descriptor;
	data->DriverData                 = NULL;

	reportDescriptor = MemoryAllocate(descriptor->OptionalDescriptors[0].Length);
	if (reportDescriptor == NULL) {
		result = ErrorMemory;
		goto deallocate;
	}

	result = UsbGetDescriptor(device, HidReport, 0, interfaceNumber, reportDescriptor,
	                          descriptor->OptionalDescriptors[0].Length,
	                          descriptor->OptionalDescriptors[0].Length, 1);
	if (result != OK) {
		MemoryDeallocate(reportDescriptor);
		LOGF("HID: Could not read report descriptor for %s.Interface%d.\n",
		     UsbGetDescription(device), interfaceNumber + 1);
		goto deallocate;
	}

	result = HidParseReportDescriptor(device, reportDescriptor,
	                                  descriptor->OptionalDescriptors[0].Length);
	if (result != OK) {
		MemoryDeallocate(reportDescriptor);
		LOGF("HID: Invalid report descriptor for %s.Interface%d.\n",
		     UsbGetDescription(device), interfaceNumber + 1);
		goto deallocate;
	}

	MemoryDeallocate(reportDescriptor);
	reportDescriptor = NULL;

	data->ParserResult->Interface = interfaceNumber;
	if ((data->ParserResult->Application.Page == GenericDesktopControl) &&
	    ((u16)data->ParserResult->Application.Desktop < HidUsageAttachCount) &&
	    (HidUsageAttach[(u16)data->ParserResult->Application.Desktop] != NULL))
		HidUsageAttach[(u16)data->ParserResult->Application.Desktop](device, interfaceNumber);

	return OK;

deallocate:
	if (reportDescriptor != NULL)  MemoryDeallocate(reportDescriptor) ;
	HidDeallocate(device);
	return result;
}

//+=============================================================================
// Func: HidGetFieldValue
// Desc: this is a made up description
//         which continues on the next line
// Args: HidParserField* field ..
//     : u32             index ..
// Retn: s32 ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
s32  HidGetFieldValue (HidParserField* field, u32 index)
{
	return BitGetSigned(field->Value.Pointer, index * field->Size, field->Size);
}
